#include "grate_test_utils.h"
#include "binary_patterns.h"
#include "grate/calculator.h"
#include "grate/dfa.h"
#include "grate/eigenvalue.h"

using growth_rate::Calculator;
using growth_rate::LexminAntiDictionary;
using growth_rate::EigenvalueOptions;
using growth_rate::binary_patterns::get_antidictionary_for_pattern;
using growth_rate::binary_patterns::is_xy_pattern;
using growth_rate::binary_patterns::build_xy_pattern;

namespace {

TEST(BinaryPatternsTest, IsPattern) {
  EXPECT_TRUE(is_xy_pattern("ababaa", "xyxyxx", 1, 1));
  EXPECT_TRUE(is_xy_pattern("aabaabaaaa", "xyxyxx", 2, 1));
  EXPECT_FALSE(is_xy_pattern("aabaabaaaa", "xyxyxx", 1, 2));
  EXPECT_TRUE(is_xy_pattern("aaaaaaaaaaaa", "xyxyxx", 2, 2));
  EXPECT_FALSE(is_xy_pattern("ababab", "xyxyxx", 1, 1));

  EXPECT_TRUE(is_xy_pattern("abaabab", "xyxxyxy", 1, 1));
  EXPECT_TRUE(is_xy_pattern("abbababbabb", "xyxxyxy", 2, 1));
  EXPECT_TRUE(is_xy_pattern("abbaabbabb", "xyxxyxy", 1, 2));
  EXPECT_FALSE(is_xy_pattern("abbaabaabb", "xyxxyxy", 1, 2));
  EXPECT_FALSE(is_xy_pattern("abbaabbab", "xyxxyxy", 1, 2));

  EXPECT_TRUE(is_xy_pattern("ababbaa", "xyxyyxx", 1, 1));
  EXPECT_TRUE(is_xy_pattern("bbabbaabbbb", "xyxyyxx", 2, 1));
  EXPECT_FALSE(is_xy_pattern("aabbaababbaaaa", "xyxyyxx", 2, 2));
}

TEST(BinaryPatternsTest, BuildPattern) {
  static const size_t kArraySize = 100;
  uint8_t array[kArraySize];
  EXPECT_EQ("aabbbbaa", build_xy_pattern(array, "xyyx", "aa", "bb").str());
  EXPECT_EQ("baaaa", build_xy_pattern(array, "yxx", "aa", "b").str());
}

struct BinaryPatternTestInput {
  const char *pattern;
  size_t root_length;
  const double exp_grate;
};

BinaryPatternTestInput kInputs[] = {
  // xyxyxx
  {"xyxyxx", 5,  1.7918013798577},
  {"xyxyxx", 6,  1.7834247888991},
  {"xyxyxx", 7,  1.7797339131099},
  {"xyxyxx", 8,  1.7774697653341},
  {"xyxyxx", 9,  1.7762514841663},
  {"xyxyxx", 10, 1.7755644146358},
  {"xyxyxx", 11, 1.7751976442565},
  {"xyxyxx", 12, 1.7749969371637},

  // xyxxyxy
  {"xyxxyxy", 4,  1.933266775326},
  {"xyxxyxy", 5,  1.932235821573},
  {"xyxxyxy", 6,  1.931879464869},
  {"xyxxyxy", 7,  1.931799595259},
  {"xyxxyxy", 8,  1.931774556943},
  {"xyxxyxy", 9,  1.931768294748},
  {"xyxxyxy", 10, 1.931766535742},

  // xyxxyxy + xxx
  {"xyxxyxy,xxx", 4,  1.303663517637},
  {"xyxxyxy,xxx", 5,  1.303663517646},
  {"xyxxyxy,xxx", 6,  1.286908203309},
  {"xyxxyxy,xxx", 7,  1.286156869057},
  {"xyxxyxy,xxx", 8,  1.286156869057},
  {"xyxxyxy,xxx", 9,  1.282825306606},
  {"xyxxyxy,xxx", 10, 1.282371986532},

  // xyxyyxx
  {"xyxyyxx", 4,  1.923661178136},
  {"xyxyyxx", 5,  1.922404278441},
  {"xyxyyxx", 6,  1.922099581774},
  {"xyxyyxx", 7,  1.922013377545},
  {"xyxyyxx", 8,  1.921989513217},
  {"xyxyyxx", 9,  1.921983691234},
  {"xyxyyxx", 10, 1.921982000651},

  // xyxyyxx + xxx
  {"xyxyyxx,xxx", 4,  1.330614503621},
  {"xyxyyxx,xxx", 5,  1.318502667867},
  {"xyxyyxx,xxx", 6,  1.318502667867},
  {"xyxyyxx,xxx", 7,  1.315157433615},
  {"xyxyyxx,xxx", 8,  1.313741973802},
  {"xyxyyxx,xxx", 9,  1.312000938606},
  {"xyxyyxx,xxx", 10, 1.311500808039},

  // xyxxyyxy
  {"xyxxyyxy", 4,  1.969352051002},
  {"xyxxyyxy", 5,  1.969173188202},
  {"xyxxyyxy", 6,  1.969149769621},
  {"xyxxyyxy", 7,  1.969145565995},
  {"xyxxyyxy", 8,  1.969144971028},
  {"xyxxyyxy", 9,  1.969144883639},
  {"xyxxyyxy", 10, 1.969144869872},

  // xyxxyyxy,xxx
  {"xyxxyyxy,xxx", 4,  1.353406731134},
  {"xyxxyyxy,xxx", 5,  1.353406731134},
  {"xyxxyyxy,xxx", 6,  1.350120338976},
  {"xyxxyyxy,xxx", 7,  1.349421711453},
  {"xyxxyyxy,xxx", 8,  1.349135717145},
  {"xyxxyyxy,xxx", 9,  1.349057885756},
  {"xyxxyyxy,xxx", 10, 1.348970949708},
};

TEST(BinaryPatternsTest, PatternGrowthRates) {
  size_t num_inputs = ARRAYSIZE(kInputs);
  for (size_t i = 0; i < num_inputs; i++) {
    LexminAntiDictionary *AD = get_antidictionary_for_pattern(
      kInputs[i].pattern, kInputs[i].root_length);
    EigenvalueOptions options;
    double grate = Calculator::get_language_growth_rate(AD, false, options);
    EXPECT_NEAR(kInputs[i].exp_grate, grate, options.precision);
    // TODO(samsonov): Check other language stats (AD size, trie size, scc
    // size etc.)
  }
}

}  // namespace
